﻿/* Copyright © 2012 Gargol, Gargol at gbot dot org
   Based on CoreDC Copyright 2007 © Johan Pettersson, Fredrik Johansson, Michael Starberg, Christian Epstein.
 
This file is part of ADCCore.

ADCCore is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

ADCCore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with ADCCore.  If not, see <http://www.gnu.org/licenses/>. */
using System;
using System.ComponentModel;
using System.Net;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Net.Sockets;


namespace ADCCore.Classes
{
    [Serializable]
    [TypeConverter(typeof(ClientInfoConverter))]
    public sealed class ClientInfo
    {
        private static readonly string defaultHostname = "adc://localhost";
        private static readonly int defaultPort = 411;
        private static readonly string defaultUsername = "-[ADCCoreDc]-";
        private string pid = "";
        private string cid = "";

        public ClientInfo()
        {
            this.Hostname = defaultHostname;
            this.Port = defaultPort;
            this.Username = defaultUsername;
            this.Password = String.Empty;
            this.Description = String.Empty;
            this.Email = String.Empty;
            this.Tag = "ADCCoreDc";
            this.ReconnectOnDisconnect = false;
            //this.Tag = new Object();
            this.Share = Sharesize.Empty;
            this.FollowRedirects = false;
            this.RespondToRevConnectToMe = false;
            this.ClientPort = 1337;
            this.PID = pid;
        }

        public enum Sharesize
        {
            Empty = 0,
            Small = 10,
            Medium = 50,
            Large = 100
        }

        /// <summary>
        /// Gets or sets the sharesize
        /// </summary>
        /// <value>The hostname</value>
        public Sharesize Share { get; set; }

        /// <summary>
        /// Gets or sets the hostname
        /// </summary>
        /// <value>The hostname</value>
        public string Hostname { get; set; }

        /// <summary>
        /// Gets or sets the local port
        /// </summary>
        /// <value>The port</value>
        public int ClientPort { get; set; }

        /// <summary>
        /// Gets or sets the port
        /// </summary>
        /// <value>The port</value>
        public int Port { get; set; }

        /// <summary>
        /// Gets or sets the username
        /// </summary>
        /// <value>The username</value>
        public string Username { get; set; }

        /// <summary>
        /// Gets or sets the password
        /// </summary>
        /// <value>The password</value>
        public string Password { get; set; }

        /// <summary>
        /// Gets or sets the description
        /// </summary>
        /// <value>The description</value>
        public string Description { get; set; }

        /// <summary>
        /// Gets or sets the email address
        /// </summary>
        /// <value>The email address</value>
        public string Email { get; set; }

        /// <summary>
        /// Gets or sets the object that contains data about the client
        /// </summary>
        /// <value>The tag</value>
        public String Tag { get; set; }

        /// <summary>
        /// Gets or sets if the client should reconnect on disconnects.
        /// </summary>
        public Boolean ReconnectOnDisconnect { get; set; }
        /// <summary>
        /// Gets or sets the object that contains data about the client
        /// </summary>
        /// <value>The data</value>
        //public Object Tag { get; set; }

        /// <summary>
        /// Gets or sets if the client should automatically disconnect from the current hub and connect to a redirected hub
        /// </summary>
        public bool FollowRedirects { get; set; }

        /// <summary>
        /// Gets or sets if the client should respond to a $RevConnectToMe with an automatic $ConnectToMe
        /// </summary>
        public bool RespondToRevConnectToMe { get; set; }

        /// <summary>
        /// Gets the local IP-address
        /// </summary>
        /// <value>The IP-address</value>
        public IPAddress ClientIP
        {
            get
            {
                // This works on both Mono and .NET , but there is a difference: it also 
                // includes the LocalLoopBack so we need to filter that one out 
                List<IPAddress> Addresses = new List<IPAddress>();

                // Obtain a reference to all network interfaces in the machine 
                NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
                foreach (NetworkInterface adapter in adapters)
                {
                    IPInterfaceProperties properties = adapter.GetIPProperties();
                    foreach (IPAddressInformation uniCast in properties.UnicastAddresses)
                    {
                        // Ignore loop-back addresses & IPv6 
                        if (!IPAddress.IsLoopback(uniCast.Address) && uniCast.Address.AddressFamily != AddressFamily.InterNetworkV6)
                            Addresses.Add(uniCast.Address);
                    }
                }

                if (Addresses.Count > 0)
                {
                    byte[] _ip = { 192, 168, 58, 10 };
                    Addresses[0] = new System.Net.IPAddress(_ip);

                    return Addresses[0];
                }

                return new IPAddress(0);
            }
        }


        /// <summary>
        /// Gets or sets complete address for hub, with hostname and port (ex. my.hub.com:411)
        /// </summary>
        /// <value>The hubadress, in the form of hostname:port</value>
        public string HubAdress
        {
            // return "adc://hostname:port"
            get { return String.Format("{0}:{1}", Hostname, Port); }
            set
            {
                //value = value.Replace("adc://","");
                //value = value.Replace("adcs://","");
                // we should be recieving "adc://hostname:port" - split, verify, and parse
                string[] address = value.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                try
                {
                    Hostname = (address.Length > 0) ? address[0] + ":" + address[1] : defaultHostname;
                }
                catch { throw new Exception("Hostname Error"); }
                var port = defaultPort;
                if (address.Length > 1)
                {
                    try
                    {
                        Int32.TryParse(address[2], out port);
                    }
                    catch { Int32.TryParse(address[1], out port); }
                }
                Port = port;
            }
        }

        public string PID
        {
            get
            {
                if (pid != "")
                    return pid;
                ADCCore.TTH.Tiger tiger = new ADCCore.TTH.Tiger();
                byte[] data = tiger.ComputeHash(System.Text.Encoding.UTF8.GetBytes(Environment.MachineName + "ADCCore++" + System.DateTime.Now.Ticks.ToString()));
                PID = Base32.Encode(data);
                /*
                data = Base32.Decode(pid);
                tiger = new TTH.Tiger();
                cid = Base32.Encode(tiger.ComputeHash(data));*/
                return pid;
            }
            set
            {
                if (value == string.Empty)
                    return;
                pid = value;
                byte[] data = Base32.Decode(value);
                ADCCore.TTH.Tiger tiger = new TTH.Tiger();
                cid = Base32.Encode(tiger.ComputeHash(data));
            }

        }
        
        public string CID
        {
            get
            {
                if (cid != "")
                    return cid;

                byte[] data = Base32.Decode(PID);
                ADCCore.TTH.Tiger tiger = new TTH.Tiger();
                return Base32.Encode(tiger.ComputeHash(data));
            }
/*            set { 
                cid = value; 

            }*/
        }
    }

    class ClientInfoConverter : TypeConverter
    {
        private static readonly string na = "[N/A]";

        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            return TypeDescriptor.GetProperties(typeof(ClientInfo));
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            var ci = (ClientInfo)value;
            if (String.IsNullOrEmpty(ci.Hostname))
            {
                return na;
            }
            return String.Format("{0}:{1}", ci.Hostname, ci.Port);
        }

    }
}
